package br.com.gaspar.framework.modelo;


import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import br.com.gaspar.framework.persistencia.IBaseDAO;
import br.com.gaspar.framework.persistencia.anotacao.ExclusaoLogica;
import br.com.gaspar.framework.persistencia.util.IJPAParams;
import br.com.gaspar.utils.AnotacaoUtil;
import br.com.gaspar.utils.ReflexaoUtil;
import br.com.gaspar.utils.entidade.EntidadeBase;
import br.com.gaspar.utils.exception.BaseException;


/**
 * Implementação base para os métodos de negócio comuns a todos os sistemas
 * @author gaspar
 */
public abstract class BaseBO<T extends EntidadeBase> implements IBaseBO<T> {
	protected static Logger log = LoggerFactory.getLogger(BaseBO.class);
	
	public abstract IBaseDAO<T> getDAOPadrao();

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	@Override
	public T editar(T entidade) throws BaseException {
		try{
			editarAntes(entidade);
			
			//chama método de verificação
			getDAOPadrao().naoDeveExistir(entidade, "E");
			
			T atualizado = getDAOPadrao().editar(entidade);
			
			editarApos(entidade);
			
			log.debug("Passou no editar - ModeloBase");
			
			return atualizado;
		
		}catch (BaseException e) {
			log.error("Erro no editar - ModeloBase " + e.getMessage());
			throw e;
		}
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de uma edição
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void editarAntes(T entidade) throws BaseException{
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre após uma edição
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void editarApos(T entidade) throws BaseException{
		
	}

	@Override
	public T gravar(T entidade) throws BaseException {
		try{
			gravarAntes(entidade);
			
			getDAOPadrao().naoDeveExistir(entidade, "G");
			
			//tratamento para exclusão lógica
			if(AnotacaoUtil.getAnotacao(entidade, ExclusaoLogica.class) != null){
				Object ret = ReflexaoUtil.executaMetodo(ReflexaoUtil.getMetodo(entidade, "getAtivo"), entidade); 
				//Se for objeto que usa exclusão lógica e o atributo ativo não tiver valor, o valor padrão true será assumido
				if(ret == null){
					ReflexaoUtil.executaMetodo(ReflexaoUtil.getMetodo(entidade, "setAtivo", Boolean.class), entidade, Boolean.TRUE);
				}
			}
			
			getDAOPadrao().gravar(entidade);
			
			gravarApos(entidade);
		}catch (BaseException e) {
			log.error("Erro no gravar - ModeloBase " + e.getDescricao());
			throw e;
		}
		return entidade;
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de uma inclusão
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void gravarAntes(T entidade) throws BaseException{
		
	}

	/**
	 * Desing Pattern Template Method, disparado sempre após uma inclusão
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void gravarApos(T entidade) throws BaseException{
		
	}

	/**
	 * 
	 */
	@Override
	public void excluir(T entidade) throws BaseException {
		try{
			excluirAntes(entidade);
			
			//tratamento para exclusão lógica
			if(AnotacaoUtil.getAnotacao(entidade, ExclusaoLogica.class) != null){
				ReflexaoUtil.executaMetodo(ReflexaoUtil.getMetodo(entidade, "setAtivo", Boolean.class), entidade, Boolean.FALSE);
				getDAOPadrao().editar(entidade);
				
			}else{
				@SuppressWarnings("unchecked")
				Class<T> clazz = ((Class<T>) entidade.getClass());
				getDAOPadrao().excluir(clazz, entidade.getId());
			}
			
			excluirApos(entidade);
		
			log.debug("Passou no apagar - ModeloBase");
		}catch (BaseException e) {
			log.error("Erro no apagar - ModeloBase " + e.getMessage());
			 throw e;
		}
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de uma exclusão
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void excluirAntes(T entidade) throws BaseException{
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre após uma exclusão
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void excluirApos(T entidade) throws BaseException{
		
	}
	
	/**
	 * 
	 */
	@Override
	public void excluir(Class<T> clazz, Long id) throws BaseException {
		try{
			excluirAntes(clazz);
			
			//tratamento para exclusão lógica
			if(AnotacaoUtil.getAnotacao(clazz, ExclusaoLogica.class) != null){
				T obj = buscarPorId(clazz, id);
				ReflexaoUtil.executaMetodo(ReflexaoUtil.getMetodo(clazz, "setAtivo", Boolean.class), clazz, Boolean.FALSE);
				getDAOPadrao().editar(obj);
				
			}else{
				getDAOPadrao().excluir(clazz, id); 
			}
			
			excluirApos(clazz);
		
			log.debug("Passou no apagar - ModeloBase");
		}catch (BaseException e) {
			log.error("Erro no apagar - ModeloBase " + e.getMessage());
			 throw e;
		}
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de uma exclusão
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void excluirAntes(Class<T> entidade) throws BaseException{
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre após uma exclusão
	 * @param entidade
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void excluirApos(Class<T> entidade) throws BaseException{
		
	}

	@Override
	
	public T buscarEntidade(IJPAParams<T> params) throws BaseException {
		try{
			T objeto = (T) getDAOPadrao().buscarEntidade(params); 
			return objeto; 
		}catch (BaseException e) {
			log.error("Erro no buscarEntidade - ModeloBase " + e.getMessage());
			throw e;
		}
	}

	@Override
	public T buscarPorId(Class<T> classePersistente, Long id) throws BaseException {
		try{
			buscarPorIdAntes(classePersistente, id);
			
			T objeto = getDAOPadrao().buscarPorId(classePersistente, id); 
			
			buscarPorIdApos(objeto, classePersistente, id);
			
			return objeto;
		}catch (BaseException e) {
			log.error("Erro no buscarPorId - ModeloBase " + e.getMessage());
			throw e;
		}
		
	}
	
	/*@Override
	public T buscarPorId(Long id) throws BaseException {
		try{
			buscarPorIdAntes(id);
			
			T objeto = getDAOPadrao().buscarPorId(id); 
			
			buscarPorIdApos(objeto, id);
			
			return objeto;
		}catch (BaseException e) {
			log.error("Erro no buscarPorId - ModeloBase " + e.getMessage());
			throw e;
		}
		
	}*/
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de uma buscaPorId
	 * @param classePersistente
	 * @param id
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarPorIdAntes(Class<T> classePersistente, Long id) throws BaseException{
		
	}
	
	protected void buscarPorIdAntes(Long id) throws BaseException{
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre após de uma buscaPorId
	 * @param objeto
	 * @param classePersistente
	 * @param id
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarPorIdApos(T objeto, Class<T> classePersistente, Long id) throws BaseException{
		
	}
	
	protected void buscarPorIdApos(T objeto, Long id) throws BaseException{
		
	}

	
	@Override
	public T buscarPorNamedQuery(String namedQuery, Object... params) throws BaseException {
		try{
			buscarPorNamedQueryAntes(namedQuery, params);
			
			T objeto = getDAOPadrao().buscarPorNamedQuery(namedQuery, params);
			
			buscarPorNamedQueryApos(objeto, namedQuery, params);
			
			return objeto;
		}catch (BaseException e) {
			log.error("Erro no buscarPorNamedQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}
	
	public Object buscarPorNamedQuery2(String namedQuery, Object... params) throws BaseException {
		try{
			Object objeto = getDAOPadrao().buscarPorNamedQuery2(namedQuery, params);
			return objeto;
		}catch (BaseException e) {
			log.error("Erro no buscarPorNamedQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}

	/**
	 * Desing Pattern Template Method, disparado sempre antes de uma buscarPorNamedQuery
	 * @param namedQuery
	 * @param params
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarPorNamedQueryAntes(String namedQuery, Object... params)
			throws BaseException {
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre após uma buscarPorNamedQuery
	 * @param objeto
	 * @param namedQuery
	 * @param params
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarPorNamedQueryApos(T objeto, String namedQuery, Object... params)
			throws BaseException {
		
	}
	
	@Override
	public T buscarPorNativeQuery(Class<T> clazz, String sql, Object... params) throws BaseException {
		try{
			T objeto = getDAOPadrao().buscarPorNativeQuery(clazz, sql, params);
			return objeto;
		}catch (BaseException e) {
			log.error("Erro no buscarPorNativeQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}
	
	@Override
	public T buscarPorNativeQuery(String sql, Object... params) throws BaseException {
		try{
			T objeto = getDAOPadrao().buscarPorNativeQuery(sql, params);
			return objeto;
		}catch (BaseException e) {
			log.error("Erro no buscarPorNativeQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}

	@Override
	public List<T> buscarTodos(IJPAParams<T> params) throws BaseException {
		try{
			List<T> lista = getDAOPadrao().buscarTodos(params); 
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodos - ModeloBase " + e.toString());
			throw e;
		}
	}

	@Override
	public List<T> buscarTodos(Class<T> clazz, String ordenacao) throws BaseException {
		try{
			buscarTodosAntes(clazz, ordenacao);
						
			List<T> lista = getDAOPadrao().buscarTodos(clazz, ordenacao); 
			
			buscarTodosApos(lista, clazz, ordenacao);
			
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodos - ModeloBase " + e.getMessage());
			throw e;
		}
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de buscarTodos
	 * @param clazz
	 * @param ordenacao
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosAntes(Class<T> clazz, String ordenacao) throws BaseException {
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre apos buscarTodos
	 * @param lista
	 * @param clazz
	 * @param ordenacao
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosApos(List<T> lista, Class<T> clazz, String ordenacao) throws BaseException {
		
	}

	@Override
	public List<T> buscarTodosPorNamedQuery(String namedQuery, Object... params) throws BaseException {
		try{
			buscarTodosPorNamedQueryAntes(namedQuery, params);
			
			List<T> lista = getDAOPadrao().buscarTodosPorNamedQuery(namedQuery, params);
			
			buscarTodosPorNamedQueryApos(lista, namedQuery, params);
			
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodosPorNamedQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de um buscarTodosPorNamedQuery
	 * @param namedQuery
	 * @param params
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosPorNamedQueryAntes(String namedQuery, Object... params) throws BaseException {
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre após um buscarTodosPorNamedQuery
	 * @param lista
	 * @param namedQuery
	 * @param params
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosPorNamedQueryApos(List<T> lista, String namedQuery, Object... params) throws BaseException {
		
	}

	@Override
	
	public List<T> buscarTodosPorNamedQuery(String namedQuery, Integer inicio, Integer tamanhoPagina, Object... params)
			throws BaseException {
		try{
			buscarTodosPorNamedQueryAntes(namedQuery, inicio, tamanhoPagina, params);
			
			List<T> lista = getDAOPadrao().buscarTodosPorNamedQuery(namedQuery, inicio, tamanhoPagina, params);
			
			buscarTodosPorNamedQueryApos(lista, namedQuery, inicio, tamanhoPagina, params);
			
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodosPorNamedQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre antes de um buscarTodosPorNamedQuery
	 * @param namedQuery
	 * @param params
	 * @param inicio
	 * @param tamanhoPagina
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosPorNamedQueryAntes(String namedQuery, Integer inicio, Integer tamanhoPagina, Object... params) throws BaseException{
		
	}
	
	/**
	 * Desing Pattern Template Method, disparado sempre após de um buscarTodosPorNamedQuery
	 * @param lista
	 * @param namedQuery
	 * @param params
	 * @param inicio
	 * @param tamanhoPagina
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosPorNamedQueryApos(List<T> lista, String namedQuery, Integer inicio, Integer tamanhoPagina, Object... params) throws BaseException{
		
	}

	@Override
	public List<T> buscarTodosPorNativeQuery(String sql, Object... params) throws BaseException {
		try{
			List<T> lista = getDAOPadrao().buscarTodosPorNativeQuery(sql, params);
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodosPorNativeQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}

	@Override
	public List<T> buscarTodosPorNativeQuery(Class<T> clazz, String sql, Object... params) throws BaseException {
		try{
			List<T> lista = getDAOPadrao().buscarTodosPorNativeQuery(clazz, sql, params);
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodosPorNativeQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}

	@Override
	public List<T> buscarTodosPorNativeQuery(Class<T> clazz, String sql, Integer inicio, Integer tamanhoPagina, Object... params) throws BaseException {
		try{
			List<T> lista = getDAOPadrao().buscarTodosPorNativeQuery(clazz, sql, inicio, tamanhoPagina, params);
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodosPorNativeQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}

	@Override
	public List<T> buscarTodosPorNativeQuery(String sql, String nomeImplicito, Object... params) throws BaseException {
		try{
			List<T> lista = getDAOPadrao().buscarTodosPorNativeQuery(sql, nomeImplicito, params);
			return lista;
		}catch (BaseException e) {
			log.error("Erro no buscarTodosPorNativeQuery - ModeloBase " + e.getMessage());
			throw e;
		}
	}
	
	/**
	 * Verifica se a entidade passada possui a anotação de exclusão lógica
	 */
	@Override
	
	public Boolean usaExclusaoLogica(T entidade) throws BaseException {
		Boolean ret = Boolean.FALSE;
		if(AnotacaoUtil.getAnotacao(entidade, ExclusaoLogica.class) != null){
			ret = Boolean.TRUE;
		}
		return ret;
	}
	
	/**
	 * 
	 * @param sql
	 * @param params
	 * @param nomeResultSetMapping
	 * @return
	 * @throws BaseException
	 */
	public T buscarPorNativeQuery(String sql, String nomeResultSetMapping, Object... params) throws BaseException {
		T obj = null;
		try{
			obj = getDAOPadrao().buscarPorNativeQuery(sql, nomeResultSetMapping, params);
		}catch (BaseException e) {
			log.error("Erro no buscarTodosPorNativeQuery - ModeloBase " + e.getMessage());
			throw e;
		}
		return obj;
	}
	
	/**
	 * 
	 */
	@Override
	public Long buscarCount(Class<T> clazz) throws BaseException {
		return getDAOPadrao().buscarCount(clazz);
	}
	
	/**
	 * 
	 */
	@Override
	public List<T> buscarTodos(Class<T> clazz, String ordenacao, Integer inicio, Integer tamanhoPagina) throws BaseException {
		buscarTodosAntes(clazz, ordenacao, inicio, tamanhoPagina);
		
		List<T> lista = getDAOPadrao().buscarTodos(clazz, ordenacao, inicio, tamanhoPagina);
		
		buscarTodosApos(lista, clazz, ordenacao, inicio, tamanhoPagina);
		
		return lista;
	}

	/**
	 * Desing Pattern Template Method, disparado sempre antes de um buscarTodos
	 * @param clazz
	 * @param ordenacao
	 * @param inicio
	 * @param tamanhoPagina
	 * 
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosAntes(Class<T> clazz, String ordenacao, Integer inicio, Integer tamanhoPagina) 
			throws BaseException {
	}
	
	
	/**
	 * Desing Pattern Template Method, disparado sempre após um buscarTodos
	 * @param lista
	 * @param clazz
	 * @param ordenacao
	 * @param inicio
	 * @param tamanhoPagina
	 * 
	 * @throws BaseException
	 * @author gaspar
	 */
	protected void buscarTodosApos(List<T> lista, Class<T> clazz, String ordenacao, Integer inicio, Integer tamanhoPagina) 
			throws BaseException {
	}
	
	/**
	 * Executa uma namedQuery que realiza um count no banco e devolve o resultado
	 */
	public Object buscarCount(String namedQuery, Object... params) throws BaseException{
		return getDAOPadrao().buscarCount(namedQuery, params);
	}
}
